import random
import uuid
from   google.appengine.api import memcache
from   google.appengine.ext import db

#========================================================================================
# ironcap.data.counters.Shard
#========================================================================================

class CounterShard(db.Model):
	""" a counter shard """
	total    = db.IntegerProperty(default = 0)
	
#========================================================================================
# ironcap.data.counters.Counter
#========================================================================================

class Counter(db.Model):
	""" typical sharded counter with configurable shards and cache options """
	reference        = db.ReferenceProperty(db.Model)
	shard_keynames   = db.StringListProperty()
	shard_count      = db.IntegerProperty(default = 1)
	shard_cache      = db.BooleanProperty(default = False)
	shard_timeout    = db.IntegerProperty(default = 60)
	
	@classmethod
	def create(cls, **kwds):
		""" create a new counter """
		# create counter
		ret = Counter(key_name = str(uuid.uuid4()))
		
		# apply keywords
		for k,v in kwds.iteritems():
			setattr(ret, k, v)	
		
		# create shards
		for n in range(0, ret.shard_count):
			shard      = CounterShard(key_name = str(uuid.uuid4()) )
			shard.put()
			ret.shard_keynames.append( shard.key().name() )
		ret.put()
		return ret	
	
	@property
	def shards(self):
		""" returns the shards for this counter """
		ret = []
		for n in self.shard_keynames:
			shard = CounterShard.get_by_key_name(n)
			if shard:
				ret.append(shard)
		return ret

	@property
	def total(self):
		""" returns this counters totals """
		ret = 0
		if self.shard_cache:
			for n in self.shard_keynames:
				total   = memcache.get(n)
				if total is None:
					shard = CounterShard.get_by_key_name(n)
					total = shard.total
					memcache.add(n, total, 60)
				ret += total
		else:
			for n in self.shard_keynames:
				shard   = CounterShard.get_by_key_name(n)
				ret += shard.total
		return ret
		
	def resize_shards(self, value):
		""" resizes the shards used for this counter """
		
		#ensure that there is at least 1 shard
		if value < 1:
			value = 1
		
		total = self.total
		for n in self.shards:
			n.delete()
		self.shard_keynames = []
		for n in range(0, value):
			shard = CounterShard( key_name = str(uuid.uuid4()) )
			if n == 0:
				shard.total = total
			shard.put()
			self.shard_keynames.append(shard.key().name())
		self.shard_count = value
		self.put()
	

	
	def increment(self, value):
		""" increments this counter by the value supplied """
		idx   		   = random.randint(0, len(self.shard_keynames) - 1)
		shard 		   = CounterShard.get_by_key_name(self.shard_keynames[idx])
		shard.total   += value
		shard.put()
	
	def delete(self):
		""" deletes this counter with associated shards """
		for n in self.shard_keynames:
			shard = CounterShard.get_by_key_name(n)
			shard.delete()
		db.delete(self.key())